using Baula.Testing.Common;
using Baula.TextGen.ActiveTemplating.Tokenizer.TokenParsers;
using Baula.TextGen.ActiveTemplating.Tokenizer.Tokens;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Baula.TextGen.ActiveTemplating.Tests.Tokenizer.TokenParsers
{
    [TestClass]
    public class BlockExpressionParserTests : TokenParserTestsBase
    {
        [TestMethod]
        public void CanStartToken_WithEmptyString_ReturnsFalse()
        {
            Assert.IsFalse(BlockExpressionParser.CanStartToken(""));
        }

        [TestMethod]
        public void CanStartToken_WithSingleCharacterString_ReturnsFalse()
        {
            Assert.IsFalse(BlockExpressionParser.CanStartToken("{"));
        }

        [TestMethod]
        public void CanStartToken_WithPrefix_ReturnsTrue()
        {
            Assert.IsTrue(BlockExpressionParser.CanStartToken("{{"));
        }

        [TestMethod]
        public void Append_OnClosedToken_ThrowsException()
        {
            SetContentAndClose("some content");
            ExceptionAssert.Expect<CannotAppendToClosedTokenException>(() => _parser.Append('x'));
        }

        [TestMethod]
        public void CanAppend_OnNewParser_WithFirstOpeningCharacter_ReturnsTrue()
        {
            var actual = _parser.CanAppend('{');
            Assert.IsTrue(actual);
        }

        [TestMethod]
        public void CanAppend_AfterFirstPrequelCharacter_WithSecondOpeningCharacter_ReturnsTrue()
        {
            _parser.Append('{');
            var actual = _parser.CanAppend('{');
            Assert.IsTrue(actual);
        }

        [TestMethod]
        public void CanAppend_AfterPrequel_WithSomeCharacter_ReturnsTrue()
        {
            SetInitialContent("");
            var actual = _parser.CanAppend('x');
            Assert.IsTrue(actual);
        }

        [TestMethod]
        public void CanAppend_AfterPrequelAndSomeCharacter_WithSomeCharacter_ReturnsTrue()
        {
            SetInitialContent("x");
            var actual = _parser.CanAppend('y');
            Assert.IsTrue(actual);
        }

        [TestMethod]
        public void CanAppend_AfterSequel_ReturnFalse()
        {
            SetContentAndClose("x");
            var actual = _parser.CanAppend('x');
            Assert.IsFalse(actual);
        }

        [TestMethod]
        public void CreateToken_AfterSingleQuote_ThrowsException()
        {
            SetInitialContent("\"");
            _parser.Append('}');  // inside string ...
            _parser.Append('}');  // ... does not close the token

            var exception = ExceptionAssert.Expect(() => _parser.CreateToken());

            Assert.IsInstanceOfType(exception, typeof(MissingEndOfStringExpressionException));
        }

        [TestMethod]
        public void CreateToken_AfterClosedSimpleString_DoesNotThrowException()
        {
            SetInitialContent("\"x\"");
            _parser.Append('}');  // outside string ...
            _parser.Append('}');  // ... does close the token

            _parser.CreateToken();

            // Assert.Pass()
        }

        [TestMethod]
        public void CreateToken_AfterClosedStringContainingEscapedQuotes_DoesNotThrowException()
        {
            SetInitialContent("\"x\"\"y\"");
            _parser.Append('}');  // outside string ...
            _parser.Append('}');  // ... does close the token

            _parser.CreateToken();

            // Assert.Pass()
        }

        [TestMethod]
        public void CreateToken_OnUnclosedToken_ThrowsException()
        {
            SetInitialContent("some content");

            ExceptionAssert.Expect<MissingEndOfBlockExpressionException>(() => _parser.CreateToken());
        }

        [TestMethod]
        public void CreateToken_OnClosedToken_DoesNotThrowException()
        {
            SetContentAndClose("some content");

            _parser.CreateToken();

            // Assert.Pass()
        }

        [TestMethod]
        public void CreateToken_OnClosedToken_ReturnsTheCorrectExpression()
        {
            SetContentAndClose("Some content\r\nwith second line");

            var token = (BlockExpressionToken)_parser.CreateToken();

            Assert.AreEqual("Some content\r\nwith second line", token.Expression);
        }

        protected override ITokenParser CreateParser()
        {
            return new BlockExpressionParser();
        }

        protected override void CloseToken(ITokenParser parser)
        {
            parser.Append('}');
            parser.Append('}');
        }
    }
}